#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<time.h>
#include<pthread.h>
#include"../common/config.h"
#include"../common/talk.h"
#include "listen.h"

struct sockaddr_in my_addr;		//Local node's IP Address
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
FILE *f_listen;			//File for logging data
char filenam[20];	//Used for Filenames (Since the node numbers form part of the filename

void *applisten();	//Application running listening aspect
void start_listen(int argc, char *argv[])	//The function which starts listening on any node. Call this from outside.
{
  int ret1, ret2, ret5;
  pthread_t appthread, thread_time, appsendrecv;
  queue_init();				//Initializes queues (Defined in talk)
  //printf("Entered start_listen\n");

  if((ret2=pthread_create( &thread_time, NULL, &time_virtual, NULL)))	//Thread which starts and keeps the local clock running for the node
   {
     printf("Thread creation failed: %d\n", ret2);
   }
  //else
    //printf("STARTED TIME_VIRT\n");

  if(argc!=2)			//Checking for parameter's passed.
    {
      fprintf(stderr, "usage: client\'s_node_no\n");
      exit(1);
    }
  //else
    //printf("Argument passed: %s\n", argv[1]);

  fopen_listen(argv[1]);

  if((ret5=pthread_create( &appsendrecv, NULL, &main_sendrecv, (void *)argv[1])))	//Thread which calls talk
   {
     printf("Thread creation failed: %d\n", ret5);
   }
  //printf("Called sendrecv");
  sleep(2);			//This sleep ensures that main_sendrecv has started for sure as this function initializes values required everywhere
  t_init = t_virt = st_time = clock();	//Initializing the time. t_virt keeps the synchronized time for the network, t_init keeps local time when t_virt was last set, t_clock keeps current local time.
  my_addr = get_my_addr();
/*
  if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1)
    {
      perror("bind");
      exit(1);
    }
  //printf("Binding done");
*/
  if((ret1=pthread_create( &appthread, NULL, &applisten, NULL)))
    {
      printf("Thread creation failed: %d\n", ret1);
    }
  pthread_join(appthread, NULL);
  close(sockfd);
}

void fopen_listen(char *nodeno)		//Function to open file logging data from listen
{
  strcpy(filenam, nodeno);
  strcat(filenam, "listens");
  if((f_listen=fopen(filenam, "w"))==NULL)
    {
      printf("Can't open file to write\n");
      exit(1);
    }
    else {
		fprintf(f_listen, "local node number, iteration (max N-1 per), real local clock, virtual local clock\n");		//Printing parameters which would eventually be printed on this file for better readability.
	}
}

void *applisten()
{
  struct sockaddr_in their_addr;
  msg_clksync *m;
  mess_wrap mw;
  msg_s *msend;
  int size_msg;
  char *msg_ch;
//  int leader = 0;
//  int ret3, ret4;
//  pthread_t appthsend, appthrecv;
  //printf("I'm inside applisten\n");
  while(1)
    {
      msg_ch = (char *)malloc(sizeof(msg_clksync)+sizeof(char));	//Messages are received in type char
      if((queue_recv(msg_ch, SEND_SYNC1, &size_msg, &their_addr))>0)     //recvmessage in queue
	{
	  m = (msg_clksync *)msg_ch;	//Typecasting the char message to the appropriate type
	  //printf("Next step will print contents of SEND_SYNC msg recvd\n");
	  //printf("\nMessage received: %d, %lu\n", m->machine, m->iteratn);
	  mw.m.machine = m->machine;			//Copying data and wrapping it
	  mw.m.iteratn = m->iteratn;
	  mw.m.type = m->type;
	  mw.m.t0 = m->t0;
	  mw.m.tm0 = m->tm0;
	  m = NULL;
	  mw.type = SEND_SYNC1;				//Making a copy of the new data
	  t_virt += clock() - t_init;			//This is the same as below but for values updated with clock()
	  tm_virt += tm_clock - tm_init;		//Incrementing the value of the virtual time on the basis of time passed from last update
	  tm_init = tm_clock;
	  mw.t1 = t_virt;
	  mw.tm1 = tm_virt;				//Storing time, t_recv
	  // Do something here if need be.
	  msend = (msg_s *)malloc(sizeof(msg_s));	//Creating a wrap over the received message with more time stamps.
	  msend->type = RECV_SYNC1;
	  msend->msw = mw;
	  msend->t2 = t_virt + clock() - t_init;
	  msend->tm2 = tm_virt + tm_clock - tm_init;	//Storing current time, t_send
	  queue_send(msend, sizeof(msg_s), their_addr); //Using the queue functions
	  free(msend);
	}
      free(msg_ch);
      msg_ch = (char *)malloc(sizeof(msg_clksync)+sizeof(char));	//Messages receive in type char
      if((queue_recv(msg_ch, LEARN_SYNC, &size_msg, &their_addr))>0)                      //Synchronizing virtual clock according to the sender clock
	{
	  //printf("Recvd LEARN_SYNC msg");
	  m = (msg_clksync *)msg_ch;
	  t_init = clock();
	  if(m->tm0 > tm_virt + tm_clock - tm_init)	//If the synchronization clock value decided is higher than current clock value, set the local virtual clock to the decided value.
	    {
		tm_virt = m->tm0;
		t_virt = m->t0;
		tm_init = tm_clock;
	    }
	  fprintf(f_listen, "%d, %lu, %llf, %llf\n", m->machine, m->iteratn, tm_init, tm_virt);
	  fflush(f_listen);		//To ensure the data in buffer is printed onto the file
	  m = NULL;
	}
      free(msg_ch);
      msg_ch = (char *)malloc(sizeof(bullyelectn)+sizeof(char));

/*The next section isnt really helpful as it hasnt been tested and has several flaws in it
      if((queue_recv(msg_ch, MT_ELECTN_INIT, &size_msg, &their_addr))>0)                    //Answer message of leader election (bully algorithm)
	{
	  //printf("Recvd MT_ELECTN_INIT msg\n");
	  bullyelectn *msg_bully;
	  msg_bully = (bullyelectn *)msg_ch;
	  if(msg_bully->self_nodeno < hno)
	    {
	      msg_bully->type = MT_ANSWERMSG;
	      //printf("Node %d is alive.\n", hno);
	      queue_send(msg_bully, sizeof(bullyelectn), their_addr);
	      fopen_bully(node[hno]);
	      if((ret3=pthread_create( &appthsend, NULL, &appsendbully, NULL)))
		{
		  printf("Thread creation failed: %d\n", ret3);
		}
	      if((ret4=pthread_create( &appthrecv, NULL, &apprecvbully, NULL)))
		{
		  printf("Thread creation failed: %d\n", ret4);
		}
	    }
	  msg_bully = NULL;
	}
      free(msg_ch);
      msg_ch = (char *)malloc(sizeof(coord)+sizeof(char));

      if((queue_recv(msg_ch, MT_COORD, &size_msg, &their_addr))>0)                          //Learning the leader from coordination message
	{
	  //printf("Recvd MT_COORD msg\n");
	  coord *msg_coord = (coord *)msg_ch;
	  leader = msg_coord->leader;
	  fprintf(f_listen, "Node: %d says Leader- Node: %d,\tIP: %s\n", hno, leader, add[leader]);
	  fflush(f_listen);
	  msg_coord = NULL;
	}
      free(msg_ch);

      msg_ch = (char *)malloc(sizeof(msg_clksync)+sizeof(char));
      if((queue_recv(msg_ch, MT_MULTICAST, &size_msg, &their_addr))>0)                                 //Synchronizing this clock according to the sender clock
	{
	  m = (msg_clksync *)msg_ch;
	  t_init = t_clock;
	  t_virt = m->t0;
	  printf("\nTime: %llf\n", t_virt);
	  m = NULL;
	}
      free(msg_ch);*/
     }
  return (NULL);
}

